home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 April: Mac OS SDK / Dev.CD Apr 97 SDK1.toast / Development Kits (Disc 1) / Macintosh Easy Open / Documentation / Developer / PowerPC / LowerToUpperCase.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-09-11  |  18.9 KB  |  605 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:         LowerToUpperCase.c 
  3.  
  4.     Contains:    Source to a FAT extension translator that converts from lower to upper case
  5.                 The source type is always 'TEXT' and the destination type is 'UPPR'.
  6.     
  7.     Copyright:    © 1994 by Apple Computer, Inc., all rights reserved.
  8.     
  9.     
  10.     Modified from LowerToUpperCaseScrap example. Filled in the file translation part.
  11.     R.Silva         9/11/95
  12. */
  13.  
  14. #include <Types.h>
  15. #include <Files.h>
  16. #include <MixedMode.h>
  17. #include <Traps.h>
  18. #include <Errors.h>
  19. #include <Memory.h>
  20. #include <Resources.h>
  21. #include <Components.h>
  22. #include <TranslationExtensions.h>
  23. #include "LowerToUpperCase.h"
  24.  
  25. //___________________________________________________________________________________________________________
  26. //
  27. // Structures
  28. //
  29.  
  30. // Our magic cookie to tell if our translation list is initialized or not
  31. #define kModificationDateMagicCookie    0x00000001
  32.  
  33. // Set up a fixed scrap translation list structure for easy access
  34. #if defined(powerc) || defined (__powerc)
  35. #pragma options align=mac68k
  36. #endif
  37. struct OurScrapTranslationList
  38. {
  39.     unsigned long    modDate;
  40.     unsigned long    groupCount;
  41.     unsigned long    sourceCount;
  42.     unsigned long    sourceEntrySize;
  43.     ScrapTypeSpec    sourceType;
  44.     unsigned long    destinationCount;
  45.     unsigned long    destinationEntrySize;
  46.     ScrapTypeSpec    desinationType;
  47. };
  48. #if defined(powerc) || defined(__powerc)
  49. #pragma options align=reset
  50. #endif
  51.  
  52. typedef struct OurScrapTranslationList OurScrapTranslationList;
  53. typedef OurScrapTranslationList *OurScrapTranslationListPtr, **OurScrapTranslationListHandle;
  54.  
  55.  
  56. // Set up a fixed file translation list structure for easy access
  57. #if defined(powerc) || defined (__powerc)
  58. #pragma options align=mac68k
  59. #endif
  60. struct OurFileTranslationList
  61. {
  62.     unsigned long    modDate;
  63.     unsigned long    groupCount;
  64.     unsigned long    sourceCount;
  65.     unsigned long    sourceEntrySize;
  66.     FileTypeSpec    sourceType;
  67.     unsigned long    destinationCount;
  68.     unsigned long    destinationEntrySize;
  69.     FileTypeSpec    desinationType;
  70. };
  71. #if defined(powerc) || defined(__powerc)
  72. #pragma options align=reset
  73. #endif
  74.  
  75. typedef struct OurFileTranslationList OurFileTranslationList;
  76. typedef OurFileTranslationList *OurFileTranslationListPtr, **OurFileTranslationListHandle;
  77.  
  78.  
  79.  
  80. //___________________________________________________________________________________________________________
  81. //
  82. // DoGetScrapTranslationList
  83. //
  84. // This routine is called by the Translation Manager when it needs to find out what formats the scrap
  85. // translator translates between.
  86. //
  87. // Enter:    self    Instance of this translator
  88. //            list    Handle to scrap translation list provided by this translator earlier, or empty handle
  89. //                    if never set before
  90. //
  91. // Exit:    list    Handle to scrap translation list filled in my this routine
  92. //            returns    Any errors that might occur
  93. //
  94. pascal ComponentResult DoGetScrapTranslationList(ComponentInstance             self, 
  95.                                                  ScrapTranslationListHandle    list)
  96. {
  97. #pragma unused(self)
  98.     OurScrapTranslationListHandle    ourList = (OurScrapTranslationListHandle) list;        // Easier to reference
  99.     OSErr                        result = noErr;
  100.  
  101.     // The list never changes here - so see if it's been initialized once by looking
  102.     // for our magic cookie
  103.     if (((*ourList)->modDate) != kModificationDateMagicCookie){
  104.         // Resize the handle to fit our entry
  105.         SetHandleSize((Handle)ourList, sizeof(OurScrapTranslationList));
  106.         if ((result = MemError()) == noErr) {
  107.             // Stuff all the values into our fixed list
  108.             (*ourList)->modDate = kModificationDateMagicCookie;                    // Magic cookie
  109.             (*ourList)->groupCount = 1;                                            // Number groups = 1
  110.             (*ourList)->sourceCount = 1;                                        // Only 1 source type
  111.             (*ourList)->sourceEntrySize = sizeof(ScrapTypeSpec);                // Currently always sizeof(ScrapTypeSpec)
  112.             (*ourList)->sourceType.format = 'TEXT';                                // Source type is always 'TEXT'
  113.             (*ourList)->sourceType.hint = 0;                                    // Don't care about this
  114.             (*ourList)->destinationCount = 1;                                    // Only 1 destination type
  115.             (*ourList)->destinationEntrySize = sizeof(ScrapTypeSpec);            // Currently always sizeof(ScrapTypeSpec)
  116.             (*ourList)->desinationType.format = 'UPPR';                            // Destination type is always 'UPPR'
  117.             (*ourList)->desinationType.hint = 0;                                // Don't care about this
  118.         }        
  119.     }
  120.         
  121.     return result;
  122. }
  123.  
  124. //___________________________________________________________________________________________________________
  125. //
  126. // DoIdentifyScrap
  127. //
  128. // This routine is called to identify a scraps contents.  In this case, just check to see if the scrap
  129. // is type TEXT, and if it is, then we know it's TEXT.
  130. //
  131. // Enter:    self        Instance of this translator
  132. //            dataPtr        Pointer to data to identify
  133. //            dataLength    Size of data referenced by dataPtr
  134. //            dataFormat    Provided format for the data referenced by dataPtr
  135. //
  136. // Exit:    dataFormat    Changed format (if applicable)
  137. //            returns        noErr is scrap contents are TEXT, otherwise noTypeErr
  138. //
  139. pascal ComponentResult DoIdentifyScrap(ComponentInstance    self, 
  140.                                        const void*             dataPtr, 
  141.                                        Size                 dataLength, 
  142.                                        ScrapType*             dataFormat)
  143. {
  144. #pragma unused(self)
  145. #pragma unused(dataPtr)
  146. #pragma unused(dataLength)
  147.     OSErr    result = noErr;
  148.     
  149.     // We only know TEXT.  See if the format is that.  If it isn't then we don't translate
  150.     if (*dataFormat != 'TEXT')
  151.         result = noTypeErr;
  152.  
  153.     return result;
  154. }
  155.  
  156. //___________________________________________________________________________________________________________
  157. //
  158. // DoTranslateScrap
  159. //
  160. // This routine is called to perform a scrap translation.  It is only called if the source type is TEXT and
  161. // if DoIdentifyScrap was called as was able to confirm that.  Also, it is only called if the destination type
  162. // is UPPR.
  163. //
  164. // This specific translation converts all lower case characters to upper case.  Leaves the rest alone.
  165. //
  166. // Enter:    self            Instance of this translator
  167. //            progressRefNum    Reference number to progress dialog
  168. //            srcDataPtr        Pointer to source data
  169. //            srcDataLength    Length of data pointed to by srcDataPtr
  170. //            srcType            Type of data pointed to by srcDataPtr (provided via DoIdentifyScrap)
  171. //            srcTypeHint        Hint for data pointed to by srcDataPtr (from ScrapTranslationList)
  172. //            dstData            Handle to place translated destination data into
  173. //            dstType            Type of data to translate srcDataPtr into
  174. //            dstTypeHint        Hint for data to translate to (from ScrapTranslationList)
  175. //
  176. // Exit:    dstData            Handle resized and filled in with translated contents
  177. //            returns            Any errors that might occur.
  178. //            
  179. pascal ComponentResult DoTranslateScrap(ComponentInstance     self, 
  180.                                         TranslationRefNum     progressRefNum,
  181.                                         const void*            srcDataPtr, 
  182.                                         Size                 srcDataLength, 
  183.                                         ScrapType             srcType, 
  184.                                         long                 srcTypeHint,
  185.                                         Handle                 dstData, 
  186.                                         ScrapType             dstType, 
  187.                                         long                 dstTypeHint)
  188. {
  189. #pragma unused(self)
  190. #pragma unused(srcType)
  191. #pragma unused(srcTypeHint)
  192. #pragma unused(dstType)
  193. #pragma unused(dstTypeHint)
  194.  
  195.     OSErr    result;
  196.     char*    currentCharacter;
  197.     char*    lastCharacter;
  198.     char*    writeCharacter;
  199.     short    count;
  200.     Boolean    canceled = false;
  201.     short    myShort;
  202.     Handle  myAdvert;
  203.     OSErr    myErr;
  204.     
  205.     myShort = OpenComponentResFile((Component)self);
  206.     myAdvert = Get1Resource('PICT', kProgressAdvertisement);
  207.     if(myAdvert != nil){
  208.         DetachResource(myAdvert);
  209.         SetTranslationAdvertisement(progressRefNum,(PicHandle) myAdvert);
  210.     }
  211.     else {
  212.         SetTranslationAdvertisement(progressRefNum, nil);
  213.     }
  214.     myErr = CloseComponentResFile(myShort);
  215.     
  216.     // Resize the destination handle to fit all the translated contents
  217.     SetHandleSize(dstData, srcDataLength);
  218.     if ((result = MemError()) == noErr)
  219.         {
  220.         // Give me a pointer to the first character and the last character in the source buffer
  221.         currentCharacter = (char*) srcDataPtr;
  222.         lastCharacter = currentCharacter + srcDataLength;
  223.         
  224.         // Give me a pointer to the first character in the destination buffer
  225.         // (lock it down first because stupid update may move memory)
  226.         HLock(dstData);
  227.         writeCharacter = (char *)*dstData;
  228.         
  229.         // Keep a count for the progress dialog
  230.         count = 0;
  231.         
  232.         // Loop through each of the characters in the buffer
  233.         while ((currentCharacter <= lastCharacter) && !canceled)
  234.             {
  235.             // Are we in the lower case ASCII range?
  236.             if (('a' <= *currentCharacter) && (*currentCharacter <= 'z'))
  237.                 {
  238.                 *writeCharacter = (*currentCharacter - ('a' - 'A'));
  239.                 }
  240.             else
  241.                 *writeCharacter = *currentCharacter;
  242.             
  243.             // Increment the progress bar
  244.             UpdateTranslationProgress(progressRefNum, ++count * (100.00 / srcDataLength), &canceled);
  245.             
  246.             // Next…
  247.             currentCharacter++;
  248.             writeCharacter++;
  249.             }
  250.             
  251.         // Let it wiggle, see it squiggle
  252.         HUnlock(dstData);
  253.         }
  254.     
  255.     return result;
  256. }
  257.  
  258. //___________________________________________________________________________________________________________
  259. //
  260. // DoGetFileTranslationList
  261. //
  262. // This routine is called by the Translation Manager when it needs to find out what formats the scrap
  263. // translator translates between.
  264. //
  265. // Enter:    self    Instance of this translator
  266. //            list    Handle to file translation list provided by this translator earlier, or empty handle
  267. //                    if never set before
  268. //
  269. // Exit:    list    Handle to file translation list filled in my this routine
  270. //            returns    Any errors that might occur
  271. // 
  272. //
  273. pascal ComponentResult DoGetFileTranslationList(ComponentInstance             self, 
  274.                                                 FileTranslationListHandle    list)
  275. {
  276. #pragma unused(self)        
  277. #pragma unused(translationList)        
  278.  
  279.     OurFileTranslationListHandle    ourList = (OurFileTranslationListHandle) list;        // Easier to reference
  280.     OSErr                        result = noErr;
  281.  
  282.     // The list never changes here - so see if it's been initialized once by looking
  283.     // for our magic cookie
  284.     if (((*ourList)->modDate) != kModificationDateMagicCookie){
  285.         // Resize the handle to fit our entry
  286.         SetHandleSize((Handle)ourList, sizeof(OurFileTranslationList));
  287.         if ((result = MemError()) == noErr) {
  288.             // Stuff all the values into our fixed list
  289.             (*ourList)->modDate = kModificationDateMagicCookie;                    // Magic cookie
  290.             (*ourList)->groupCount = 1;                                            // Number groups = 1
  291.             (*ourList)->sourceCount = 1;                                        // Only 1 source type
  292.             (*ourList)->sourceEntrySize = sizeof(FileTypeSpec);                    // Currently always sizeof(FileTypeSpec)
  293.             (*ourList)->sourceType.format = 'TEXT';                                // Source type is always 'TEXT'
  294.             (*ourList)->sourceType.hint = 0;                                    // Don't care about this
  295.             (*ourList)->sourceType.flags = 0;
  296.             (*ourList)->sourceType.catInfoType = 'TEXT';
  297.             (*ourList)->sourceType.catInfoCreator = 'ttxt';
  298.             (*ourList)->destinationCount = 1;                                    // Only 1 destination type
  299.             (*ourList)->destinationEntrySize = sizeof(FileTypeSpec);            // Currently always sizeof(FileTypeSpec)
  300.             (*ourList)->desinationType.format = 'UPPR';                            // Destination type is always 'UPPR'
  301.             (*ourList)->desinationType.hint = 0;                                // Don't care about this
  302.             (*ourList)->desinationType.flags = 0;
  303.             (*ourList)->desinationType.catInfoType = 'UPPR';
  304.             (*ourList)->desinationType.catInfoCreator = 'PPd√';                    //??
  305.         }        
  306.     }
  307.         
  308.     return result;
  309. }
  310.  
  311. //___________________________________________________________________________________________________________
  312. //
  313. // DoIdentifyFile
  314. //
  315. // This routine is called to identify a file by its contents.  
  316. // Here we simply decide the fileType is 'TEXT'.
  317. //
  318. // Enter:    self        Instance of this translator
  319. //            theDoc        file FSSpec
  320. //
  321. // Exit:    docKind        the fileType we decided it was
  322. //            returns        noErr 
  323. //
  324. //
  325. pascal ComponentResult DoIdentifyFile(ComponentInstance self, const FSSpec *theDoc, FileType *docKind)
  326. {
  327. #pragma unused(self)
  328. #pragma unused(theDoc)
  329. #pragma unused(docKind)
  330.     OSErr myErr = noErr;
  331.     
  332.     //not doing too much here
  333.     
  334.     *docKind = 'TEXT';
  335.     
  336.     return myErr;
  337. }
  338.  
  339. //___________________________________________________________________________________________________________
  340. //
  341. // DoTranslateFile
  342. //
  343. // This routine is called to perform a file translation.  It is only called if the file type is TEXT and
  344. // if DoIdentifyFile was called as was able to confirm that.  Also, it is only called if the destination type
  345. // is UPPR.
  346. //
  347. // This specific translation converts all lower case characters to upper case.  Leaves the rest alone.
  348. //
  349. // Enter:    self            Instance of this translator
  350. //            progressRefNum    Reference number to progress dialog
  351. //            srcDoc            Pointer to source file
  352. //            srcType            fileType provided from the DoIdentifyFile
  353. //            srcTypeHint        Hint for fileType pointed to by srcDoc (from GetTranslationList)
  354. //            dstDoc            Pointer to destination file spec
  355. //            dstType            file Type of destination file
  356. //            dstTypeHint        Hint for fileType to translate to (from GetTranslationList)
  357. //
  358. // Exit:    dstDoc            File is filled in with translation of data in source file
  359. //            returns            Any errors that might occur.
  360. //            
  361. //
  362. pascal ComponentResult DoTranslateFile(ComponentInstance self, TranslationRefNum progressRefNum,
  363.                                             const FSSpec* srcDoc, FileType srcType, long srcTypeHint, 
  364.                                             const FSSpec* dstDoc, FileType dstType, long dstTypeHint)
  365. #pragma unused(self)                        
  366. #pragma unused(progressRefNum)
  367. #pragma unused(srcDoc)
  368. #pragma unused(srcType)
  369. #pragma unused(srcTypeHint)
  370. #pragma unused(dstDoc)
  371. #pragma unused(dstType)
  372. #pragma unused(dstTypeHint)
  373.  
  374.     OSErr     myErr = noErr;
  375.     short    srcRefNum, dstRefNum;
  376.     char    copyBuffer[512];
  377.     long    copyBufferSize;
  378.     Handle    myAdvert;
  379.     short    myShort;
  380.     
  381.     copyBufferSize = 512;
  382.     
  383.     myErr = FSpOpenDF( srcDoc, fsRdPerm, &srcRefNum ); 
  384.     if( myErr == noErr ){
  385.         myErr = FSpOpenDF( dstDoc, fsRdWrPerm, &dstRefNum ); 
  386.         if( myErr == noErr ){
  387.             myShort = OpenComponentResFile((Component)self);
  388.             myAdvert = Get1Resource('PICT', kProgressAdvertisement);
  389.             if(myAdvert != nil){
  390.                 DetachResource(myAdvert);
  391.                 SetTranslationAdvertisement(progressRefNum,(PicHandle) myAdvert);
  392.             }
  393.             else {
  394.                 SetTranslationAdvertisement(progressRefNum, nil);
  395.             }
  396.             myErr = CloseComponentResFile(myShort);
  397.             myErr = CopyFork(*srcDoc, srcRefNum, dstRefNum, ©Buffer, copyBufferSize,progressRefNum );
  398.             
  399.         }
  400.     }
  401.     FSClose(srcRefNum);
  402.     FSClose(dstRefNum);
  403.     
  404.     
  405.     return myErr;
  406. }
  407.  
  408. //___________________________________________________________________________________________________________
  409. //
  410. // DoGetTranslatedFilename
  411. //
  412. // 
  413. //
  414. pascal ComponentResult DoGetTranslatedFilename(ComponentInstance     self,
  415.                                                FileType                dstType,
  416.                                                long                    dstTypeHint,
  417.                                                FSSpec*                 theDocument)
  418. {
  419. #pragma unused(self);
  420. #pragma unused(dstType);
  421. #pragma unused(dstTypeHint);
  422. #pragma unused(theDocument);
  423.     Str63 theString;
  424.     FSSpec mySpec;
  425.     OSErr  myErr = noErr;
  426.     short  i = 0;
  427.     
  428.     
  429.     
  430.     BlockMove( theDocument->name, theString, 64);
  431.     theDocument->name[0] = theDocument->name[0] + 3;
  432.     theDocument->name[1] = 'U';
  433.     theDocument->name[2] = 'U';
  434.     theDocument->name[3] = 'P';
  435.  
  436.     BlockMove( theString + 1, theDocument->name + 4, 60);
  437.     
  438.     myErr = FSMakeFSSpec( theDocument->vRefNum, theDocument->parID, theDocument->name, &mySpec);
  439.     //quick and dirty way of naming, some error checking should be done so no illegal characters are used
  440.     while( myErr != fnfErr ){
  441.         theDocument->name[3] = (char)(theDocument->name[3]) + 1;
  442.         myErr = FSMakeFSSpec( theDocument->vRefNum, theDocument->parID, theDocument->name, &mySpec);
  443.     }
  444.     
  445.     if( myErr == fnfErr ){
  446.         myErr = noErr;
  447.     }
  448.     
  449.     return myErr;
  450.     
  451. }
  452. //
  453. //
  454. //  Took this function out of MoreFiles and modified it a bit
  455. //  MoreFiles can be found on the ToolChest edition of the Developer CDs
  456. //
  457. pascal    OSErr    CopyFork(FSSpec srcSpec, short srcRefNum,
  458.                          short dstRefNum,
  459.                          void *copyBufferPtr,
  460.                          long copyBufferSize, TranslationRefNum progressRefNum)
  461. {
  462.     ParamBlockRec srcPB;
  463.     ParamBlockRec dstPB;
  464.     OSErr srcError;
  465.     OSErr dstError;
  466.     Boolean canceled = false;
  467.     short    myPercent = 0;
  468.     short    yourPercent;
  469.     unsigned short  numBlocks;
  470.     long    myBlockSize;
  471.     
  472.     char*    currentCharacter;
  473.     char*    lastCharacter;
  474.  
  475.     if ( (copyBufferPtr == NULL) || (copyBufferSize == 0) )
  476.         return ( paramErr );
  477.     
  478.     srcPB.ioParam.ioRefNum = srcRefNum;
  479.     dstPB.ioParam.ioRefNum = dstRefNum;
  480.  
  481.     // preallocate the destination fork and 
  482.     // ensure the destination fork's EOF is correct after the copy 
  483.     srcError = PBGetEOFSync(&srcPB);
  484.     if ( srcError != noErr )
  485.         return ( srcError );
  486.     dstPB.ioParam.ioMisc = srcPB.ioParam.ioMisc;
  487.     dstError = PBSetEOFSync(&dstPB);
  488.     if ( dstError != noErr )
  489.         return ( dstError );
  490.     
  491.     
  492.     
  493.     // If copyBufferSize is greater than 512 bytes, make it a multiple of 512 bytes 
  494.     // This will make writes on local volumes faster
  495.     
  496.     myBlockSize = ((copyBufferSize >= 512) && (copyBufferSize % 512)) ?
  497.                                (copyBufferSize / 512) * 512 :
  498.                                (copyBufferSize);
  499.     
  500.     //need to know the size of the file to give user feedback 
  501.     //thru the progress dialog box. So here we count the blocks
  502.     
  503.     srcError = CountTheBlocks(srcSpec.vRefNum,
  504.                                        srcSpec.parID,
  505.                                        srcSpec.name, myBlockSize,
  506.                                        &numBlocks);    
  507.     if( srcError == noErr ){
  508.         myPercent = numBlocks;
  509.     }
  510.     else{
  511.         myPercent = 0;
  512.     }
  513.  
  514.     // reset source fork's mark 
  515.     srcPB.ioParam.ioPosMode = fsFromStart;
  516.     srcPB.ioParam.ioPosOffset = 0;
  517.     srcError = PBSetFPosSync(&srcPB);
  518.     if ( srcError != noErr )
  519.         return ( srcError );
  520.  
  521.     // reset destination fork's mark 
  522.     dstPB.ioParam.ioPosMode = fsFromStart;
  523.     dstPB.ioParam.ioPosOffset = 0;
  524.     dstError = PBSetFPosSync(&srcPB);
  525.     if ( dstError != noErr )
  526.         return ( dstError );
  527.  
  528.     // set up fields that won't change in the loop 
  529.     srcPB.ioParam.ioBuffer = (Ptr)copyBufferPtr;
  530.     srcPB.ioParam.ioPosMode = fsAtMark + 0x0020;// fsAtMark + noCacheBit 
  531.     srcPB.ioParam.ioReqCount = myBlockSize;
  532.     
  533.     
  534.  
  535.     dstPB.ioParam.ioBuffer = (Ptr)copyBufferPtr;
  536.     dstPB.ioParam.ioPosMode = fsAtMark + 0x0020;// fsAtMark + noCacheBit 
  537.     yourPercent = 0;
  538.     while ( (srcError == noErr) && (dstError == noErr) )
  539.     {
  540.         srcError = PBReadSync(&srcPB);
  541.         dstPB.ioParam.ioReqCount = srcPB.ioParam.ioActCount;
  542.         
  543.         currentCharacter = (char*)copyBufferPtr;
  544.         lastCharacter = currentCharacter + srcPB.ioParam.ioActCount;
  545.         
  546.         while ((currentCharacter <= lastCharacter) && !canceled)
  547.             {
  548.             // Are we in the lower case ASCII range?
  549.             if (('a' <= *currentCharacter) && (*currentCharacter <= 'z'))
  550.                 {
  551.                 *currentCharacter = (*currentCharacter - ('a' - 'A'));
  552.                 }
  553.             
  554.             
  555.             // Next…
  556.             currentCharacter++;
  557.             
  558.             }
  559.             
  560.         
  561.         dstError = PBWriteSync(&dstPB);
  562.         yourPercent++;
  563.         UpdateTranslationProgress(progressRefNum, ((yourPercent * 100)/myPercent) , &canceled);
  564.         
  565.     }
  566.  
  567.     // make sure there were no errors at the destination 
  568.     if ( dstError != noErr )
  569.         return ( dstError );
  570.  
  571.     // make sure the only error at the source was eofErr 
  572.     if ( srcError != eofErr )
  573.         return ( srcError );
  574.  
  575.     return ( noErr );
  576. }
  577. static    OSErr    CountTheBlocks(short srcVRefNum,
  578.                                        long srcDirID,
  579.                                        ConstStr255Param srcName, long theBlockSize,
  580.                                        unsigned short *numDataBlks)
  581. {
  582.     HParamBlockRec pb;
  583.     OSErr error;
  584.     unsigned short srcDataBlks;
  585.     
  586.     
  587.     // get the size of the file's data resource fork 
  588.     pb.fileParam.ioNamePtr = (StringPtr)srcName;
  589.     pb.fileParam.ioVRefNum = srcVRefNum;
  590.     pb.fileParam.ioFVersNum = 0;
  591.     pb.fileParam.ioDirID = srcDirID;
  592.     pb.fileParam.ioFDirIndex = 0;
  593.     error = PBHGetFInfoSync(&pb);
  594.     if ( error == noErr )
  595.     {
  596.     // get number of 512-byte blocks needed for data fork 
  597.     srcDataBlks = ((unsigned long)pb.fileParam.ioFlLgLen % 512) ?
  598.                   (((unsigned long)pb.fileParam.ioFlLgLen >> 9) + 1) :
  599.                   ((unsigned long)pb.fileParam.ioFlLgLen >> 9);
  600.     }
  601.     
  602.     *numDataBlks = ( srcDataBlks *  512 )/ theBlockSize;
  603.     return ( error );
  604. }